package ls.jpa.chapter6.dao;

import ls.jpa.chapter6.entity.UserInfo;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.*;
import org.springframework.data.jpa.repository.query.Procedure;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.LockModeType;
import javax.persistence.QueryHint;
import java.util.List;

/**
 * @author Barudisshu
 */
@Transactional
public interface UserInfoDao extends JpaRepository<UserInfo, Integer> {

    String MySQL = "SELECT * FROM t_user u WHERE u.username = (?0)";

    @Override
    UserInfo findOne(Integer integer);

    @Override
    List<UserInfo> findAll();

    @Override
    Page<UserInfo> findAll(Pageable pageable);
    /**
     * 本地查询测试，MySQL参数下标从0开始
     *
     * @param username 用户名
     * @return 唯一用户
     */
    @Query(nativeQuery = true, value = MySQL)
    @Cacheable(value = "defaultMemcachedClient", key = "'userinfo_'+#username")
    UserInfo findByName(String username);

    /**
     * JPQL查询测试
     *
     * @param id 用户Id
     * @return 用户
     */
    @Query("select u from USER u where u.id = :id")
    @Cacheable(value = "entityCache", key = "'userinfo_'+#id")
    UserInfo findById(@Param("id") Long id);

    /** 高级查询 */
    /**
     * 分页功能，由于分页内容可能过多，指定一个缓存，指定缓存为hibernate二级缓存
     *
     * @param age      年龄
     * @param pageable 是否分页
     * @return 页
     */
    @QueryHints(value = {@QueryHint(name = "org.hibernate.cacheable", value = "true")}, forCounting = false)
    Page<UserInfo> findByAge(@Param("age") Integer age, Pageable pageable);

    @Query("from USER")
    @QueryHints(value = {@QueryHint(name = "org.hibernate.cacheable", value = "true")})
    List<UserInfo> findAllCached();

    // Modifying 方法注解只可以返回void，Integer和int------------------
    // 更新查询语句必须实现事务操作，用@Transactional标识---------------

    /**
     * 注解参数方式，使用@Param标识
     *
     * @param username 用户名
     * @param password 密码
     * @return 被更新的行
     */
    @Modifying
    @Query("update USER u set u.password=:password where u.username=:username")
    Integer updatePassword(@Param("username") String username, @Param("password") String password);

    /**
     * 位置参数方式，JPQL参数下标从1开始
     *
     * @param id       主键
     * @param username 用户名
     * @return 被更新的行
     */
    @Modifying
    @Query("update USER u set u.username=?2 where u.id=?1")
    Integer updateUsername(Long id, String username);

    /**-------------------------------------
     *              高级进阶                |
     --------------------------------------*/

    // SpEL查询语句，注意两种方式的异同---------------------------------

    /**
     * 非参数注解方式
     */
    @Query("select u from USER u where u.age=?#{[0]}")
    UserInfo findByAge(Integer age);

    /**
     * 参数注解方式
     */
    @Query(value = "select u from USER u where u.password=:#{#userInfo.password}")
    UserInfo findByPassword(@Param("userInfo") UserInfo userInfo);

    /**复杂查询*/
    /**
     * Spring-data提供了Spring-Security权限管理查询支持，由于本系统没有用到，暂时不提供例子.
     */

    // 调用存储过程，注意两种方式的异同---------------------------------
    @Procedure("pro_plus")
    Integer explicityNumber(Integer arg);

    @Procedure(name = "USER.plus", procedureName = "pro_plus")
    Integer plus(@Param("arg") Integer arg);

    // 事务处理------------------------------------------------------
    public UserInfo saveWithOption(UserInfo userInfo);

    // 加锁进行crud操作 ----------------------------------------------
    @Lock(LockModeType.PESSIMISTIC_READ)
    @Override
    long count();
}
